package com.study.springbootsecurity.util;

import com.study.springbootsecurity.config.properties.JwtConfigProperties;
import com.study.springbootsecurity.constants.JwtTokenConstants;
import com.study.springbootsecurity.entity.Role;
import com.study.springbootsecurity.entity.SysUser;
import com.study.springbootsecurity.exception.BusinessException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @Description: TODO token工具类
 * @author: Huangjianyun
 * @date: 2025-06-04 20:02
 */
@Component
@Log4j2
public class TokenUtil {

//    @Value("${app.secret}")
//    private static String appSecret;
//
//    @Value("${app.issue}")
//    private static  String appIssue;
//    @Autowired
//    private static JwtConfigProperties jwtConfigProperties;



    /**
     * 根据用户信息生成默认有效期(5分钟)的JWT Token
     * 这里有个坑 设置claim覆盖之前jwt默认claims 所以设置claim放在最前面
     * @param user 用户实体，需包含ID、用户名和身份信息
     * @return 生成的JWT Token字符串
     */
//    public static String createToken(SysUser user,JwtConfigProperties jwtConfigProperties) {
//        DefaultClaims claims = new DefaultClaims();
//        claims.put("roles",getRoleNames(user));
//        claims.put("username",user.getUsername());
//
//        return Jwts.builder()
//                .setClaims(claims)
//                .setId(user.getId().toString())  // tokenId
//                .setSubject(user.getUsername())  // token主题
//                .setIssuedAt(new Date())         // token签发时间
//                .setIssuer(jwtConfigProperties.getIssue())             // token签发者
//                .setExpiration(new Date(System.currentTimeMillis() + 5*60*1000)) //token的有效期5min
//                // 使用HS256签名和密钥
//                .signWith(new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(),SignatureAlgorithm.HS256.getJcaName()), SignatureAlgorithm.HS256)
//                .compact();
//    }

    /**
     * 根据用户信息和指定过期时间生成访问令牌 JWT Token
     *
     * @param user 用户实体
     * @return 生成的JWT Token字符串
     */
    public static String createToken(SysUser user, JwtConfigProperties jwtConfigProperties) {
        DefaultClaims claims = new DefaultClaims();
        claims.put("roles",getRoleNames(user));
        claims.put("username",user.getUsername());
        SecretKeySpec secretKeySpec = new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(), SignatureAlgorithm.HS256.getJcaName());
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date expirationTime = new Date(System.currentTimeMillis()+jwtConfigProperties.getExpiration());
        try {
            expirationTime = f.parse(f.format(expirationTime));
        } catch (ParseException e) {
            throw new BusinessException("token过期时间格式化失败~~~~~");
        }

        return Jwts.builder()
                .setClaims(claims)
                .setId(user.getId().toString())      //设置tokenId
                .setSubject(user.getUsername())      //设置token主题
                .setIssuer(jwtConfigProperties.getIssue())                 // 设置token签发者
                .setIssuedAt(new Date())             // 设置token颁发时间
                .setExpiration(expirationTime)       // 设置token有效期
                .signWith(secretKeySpec, SignatureAlgorithm.HS256)
                .compact();
    }
    /**
     * 生成刷新令牌token
     *
     * @param username 用户名
     * @param jwtConfigProperties jwt读取配置文件对象
     * @param deviceFingerprint  设备指纹
     * @return 刷新令牌
     */
    public static String createRefreshToken(String username, JwtConfigProperties jwtConfigProperties,String deviceFingerprint) {
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 构建设备指纹 claims存储设备指纹和用户名
        Claims claims = new DefaultClaims();
        claims.put("dp",deviceFingerprint);
        claims.put("username",username);
        //jwtConfigProperties.getRefreshExpiration() 存储的秒数 转换毫秒数乘1000
        Date refreshExpireTime = new Date(System.currentTimeMillis()+jwtConfigProperties.getRefreshExpiration()*1000);
        try {
            refreshExpireTime = f.parse(f.format(refreshExpireTime));
        } catch (ParseException e) {
            throw new BusinessException("token过期时间格式化失败~~~~~");
        }

        return Jwts.builder()
                .setClaims(claims)
                .setSubject(username)      //设置token主题
                .setIssuer(jwtConfigProperties.getIssue()) // 设置token签发者
                .setIssuedAt(new Date())             // 设置token颁发时间
                .setExpiration(refreshExpireTime)       // 设置token有效期
                .signWith(new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(),SignatureAlgorithm.HS256.getJcaName()), SignatureAlgorithm.HS256)
                .compact();
    }


    /**
     * 从Token中获取用户名
     *
     * @param token JWT Token字符串
     * @return 用户名，如果Token无效则返回null
     */
    public static String getUsernameFromToken(String token,JwtConfigProperties jwtConfigProperties) {
        SecretKeySpec appSecretKey = new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(), SignatureAlgorithm.HS256.getJcaName());
        return Jwts.parserBuilder()
                .setSigningKey(appSecretKey)
                .build()
                .parseClaimsJws(token).getBody()
                .getSubject();
    }

    /**
     * 判断Token是否存在且过期(方式1：直接校验Token字符串)
     *
     * @param token JWT Token字符串
     * @return true=有效，false=无效
     */
    public static boolean checkToken(String token,JwtConfigProperties jwtConfigProperties) {
        SecretKeySpec appSecretKey = new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(), SignatureAlgorithm.HS256.getJcaName());
        if (StringUtils.isBlank(token)) {
            return false;
        }
        try {
            Jwts.parserBuilder()
                    .setSigningKey(appSecretKey)  //设置密钥key
                    .build()
                    .parseClaimsJws(token);
            return true;
        }catch (ExpiredJwtException e) {
            log.error("Token已过期: " + e.getMessage());
            return false;
        } catch (SignatureException e) {
            log.error("Token签名无效: " + e.getMessage());
            return false;
        } catch (MalformedJwtException e) {
            log.error("Token格式错误: " + e.getMessage());
            return false;
        } catch (UnsupportedJwtException e) {
            log.error("不支持的JWT类型: " + e.getMessage());
            // 打印详细信息，帮助调试
            log.error("令牌内容: " + token);
            return false;
        } catch (IllegalArgumentException e) {
            log.error("Token参数错误: " + e.getMessage());
            return false;
        }
    }


    /**
     * 判断Token是否存在且有效(方式2：从HTTP请求中获取Token校验)
     *
     * @param request HTTP请求对象
     * @return true=有效，false=无效
     */
    public static boolean checkToken(HttpServletRequest request,JwtConfigProperties jwtConfigProperties) {
        try {
            // 从请求头中获取Token
            String token = request.getHeader(JwtTokenConstants.JWT_TOKEN_HEADER);
            return checkToken(token,jwtConfigProperties);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("token解析失败~~~~~~~");
            return false;
        }
    }

    /**
     * 解析JWT Token获取所有声明信息
     *
     * @param token JWT Token字符串
     * @return 包含所有声明的Claims对象
     */
    public static Claims parseJwt(String token,JwtConfigProperties jwtConfigProperties) {
        SecretKeySpec appSecretKey = new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(), SignatureAlgorithm.HS256.getJcaName());

        return Jwts.parserBuilder()
                .setSigningKey(appSecretKey)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从Token中获取指定声明的值
     *
     * @param token JWT Token字符串
     * @param key 声明的键名(如"identity")
     * @return 声明的值，以字符串形式返回
     */
    public static String getTokenClaim(String token, String key,JwtConfigProperties jwtConfigProperties) {
        SecretKeySpec appSecretKey = new SecretKeySpec(jwtConfigProperties.getSecret().getBytes(), SignatureAlgorithm.HS256.getJcaName());

        Claims body = Jwts.parserBuilder()
                .setSigningKey(appSecretKey)
                .build()
                .parseClaimsJws(token)
                .getBody();
        return String.valueOf(body.get(key));
    }


    /**
     * 从Token中获取指定声明的值
     *
     * @param body 解析后JWT Token字符串
     * @return 声明的值，以字符串形式返回
     */
    public static String getSubjectByClaim(Claims body) {
        return String.valueOf(body.getSubject());
    }

    public static String getRoleNames(SysUser user) {
        List<Role> roles = user.getRoles();
        List<String> roleNameList = roles.stream().map(Role::getRoleName).toList();
        return "[" + String.join(",",roleNameList) + "]";
    }


}
